package fauxman.states;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

import fauxman.DynamicElements.Ghost;
import fauxman.Enums.StateType;
import fauxman.StaticElements.PlayableArea;
import fauxman.model.WorldModel;
import fauxman.utilities.ActionUtil;

import java.awt.Point;

public abstract class GhostState {
	protected StateType myStateType = StateType.GHOST_STATE;
	
	public void draw(Graphics g, Ghost ghost) {
		g.drawImage(ghost.getAppearance(), (int)ghost.getContinuousPosition().getX(), (int)ghost.getContinuousPosition().getY(), ghost.getWidth(), ghost.getHeight(), null); //TODO: look into Observer
		g.setColor(Color.GREEN);
		g.fillRect((int)ghost.getConHitbox().getX(),(int)ghost.getConHitbox().getY(),(int)ghost.getConHitbox().getWidth(),(int)ghost.getConHitbox().getHeight());
	}
	
	public abstract void setAppearance(int appearanceID, Ghost g);
	public abstract int[] getTarget(WorldModel model, Ghost g);

	/* If a new move needs to be generated, we set it here. This is to facilitate
	 * reverseDirection so it does not get overwritten.
	 */
	public int[] getNextDirection(WorldModel model, Ghost ghost) {
		int[] dir = null;
		
//System.out.println("disPos = ("+ghost.getDiscretePosition().x+","+ghost.getDiscretePosition().y+") , revBlock = ("+ghost.getReversalBlock()[0]+","+ghost.getReversalBlock()[1]+")");
		if((ghost.getDiscretePosition().x == ghost.getReversalBlock()[0]) && (ghost.getDiscretePosition().y == ghost.getReversalBlock()[1]))
		{
//System.out.println("GHOST: "+ ghost +", BEFORE:: currentAction: "+ ghost.getCurrentAction() +" ; nextAction: "+ ghost.getNextAction());
			reverseDirection(model, ghost);
//System.out.println("GHOST: "+ ghost +", AFTER:: currentAction: "+ ghost.getCurrentAction() +" ; nextAction: "+ ghost.getNextAction());
			ghost.setReversalBlock(-1, -1); //Reset the reversal block
			return ghost.getCurrentDirection();//Returning currentAction instead of nextAction because we've set currentAction and nextAction based on our reversal logic
		}
//System.out.println("ghost state lookahead: "+ghost.getLookAhead());
		//only calculate a move if we are at an intersection and we havent already calculated one for this intersection
		
		if(ghost.getDiscretePosition().x ==13&& ghost.getDiscretePosition().y == 23 ){
			System.out.println("foo");
		}
		
		if(!ghost.getShouldCalculateNextDirection(ghost.getDiscretePosition().x, ghost.getDiscretePosition().y, ghost.getLookAhead().x,ghost.getLookAhead().y) || !ghost.isIntersection((int)ghost.getLookAhead().getX(),(int)ghost.getLookAhead().getY()) ||  (ghost.getCurrentIntersection().equals(ghost.getLookAhead()))){ 
//System.out.println("isIntersection: "+ghost.getNextAction());
			return ghost.getNextDirection();
		}
		
		dir = coreGhostAI(model, ghost);
		
		ghost.setCurrentIntersection(ghost.getLookAhead());
		
		ghost.setNextDirection(dir);
		
		return ghost.getNextDirection();
	}

	protected int[] coreGhostAI(WorldModel model, Ghost ghost) {
		int[] dir = null;
		int[] target = ghost.getTarget();
		ArrayList<int[]> choices = ghost.validIntersectionDirections((int)ghost.getLookAhead().getX(),(int)ghost.getLookAhead().getY());
		double bestDiff = 1000000000;
		int gX = (int) ghost.getDiscretePosition().getX();
		int gY = (int) ghost.getDiscretePosition().getY();
		double currentDiff;
		Point currentP;
		int pX,pY;
		for(int[] d: choices)
		{
			currentP = ActionUtil.getNextPosition(ghost.getLookAhead(), ActionUtil.DISCRETE_UNITS, d);
			pX = (int)currentP.getX();
			pY = (int)currentP.getY();
			if(!(gY == pY && gX == pX))
			{
				currentDiff = Math.sqrt(( Math.pow(target[0] - pX, 2) + Math.pow(target[1] - pY, 2)));
				if(currentDiff < bestDiff)
				{
					bestDiff = currentDiff;
					dir = d;
				}
			}
		}
		return dir;
	}
	
	/* Ghosts reverse when changing modes, except if changing from vulnerable. They change direction discretely. Once a reversal is triggered, we set a reversal block to be the current lookAhead position.
	 * Normal reversal case, in which lookAhead is not at an intersection after reversal -> Reverse currentAction and set nextAction to currentAction
	 * Intersection reversal case, in which lookAhead is at an intersection after reversal -> Reverse currentAction and generate nextAction
	 * The reason for Intersection case is because otherwise the ghost will not make a direction decision.
	 * ASSUMPTIONS: Ghost is already on a reversal block.
	 */
	public void reverseDirection(WorldModel model, Ghost g){
//System.out.println("gost: "+g+" is reversing direction");
		int[] dir = null;
		g.setCurrentIntersection(new Point(-1,-1));
		if(Arrays.equals(g.getCurrentDirection(), ActionUtil.NORTH)){
			dir = ActionUtil.SOUTH;
		}else if(Arrays.equals(g.getCurrentDirection(), ActionUtil.SOUTH)){
			dir = ActionUtil.NORTH;
		}else if(Arrays.equals(g.getCurrentDirection(), ActionUtil.WEST)){
			dir = ActionUtil.EAST;
		}else if(Arrays.equals(g.getCurrentDirection(), ActionUtil.EAST)){
			dir = ActionUtil.WEST;
		}
		g.setCurrentDirection(dir);
		if(model.isPlayableAreaIntersection(g.getDiscretePosition().x,g.getDiscretePosition().y)){
			g.setLookAhead(g.getDiscretePosition()); //Why
			g.setNextDirection(coreGhostAI(model, g));
		}
		else {
			g.setNextDirection(g.getCurrentDirection());
		}
		Point lookAhead = ActionUtil.getNextPosition(g.getDiscretePosition(), ActionUtil.DISCRETE_UNITS, g.getCurrentDirection());
//System.out.println(this+" curAct after: "+g.getCurrentAction());
//System.out.println(this+" nextAct after: "+g.getNextAction());
//System.out.println("reversal "+this+" lookahead: "+lookAhead);
		g.setLookAhead(lookAhead);
		if(model.isPlayableAreaIntersection(g.getLookAhead().x,g.getLookAhead().y)){
			g.setNextDirection(coreGhostAI(model, g));
//System.out.println("nextAction22222: "+g.getNextAction());
		}
	}

	public Collection<? extends PlayableArea> getPlayableArea(WorldModel model) {
		return model.getPlayableArea();
	}
	
	public void reactToCurrentDiscretePosition(WorldModel model, Ghost g){
		// TODO: No-up zone
		// TODO: Chunnel
		
	}

	public ArrayList<int[]> validIntersectionDirections(WorldModel model, int x, int y) {
		return model.validPlayableAreaIntersectionDirections(x, y);
	}

	public boolean isIntersection(WorldModel model, int x, int y) {
		return model.isPlayableAreaIntersection(x,y);
	}
	
	public StateType getStateType() {
		return myStateType;
	}
	
	//DisX and DisY are the current ghosts discrete position.  This is for NoUp mode.
	public boolean shouldCalculateNextDirection(WorldModel model, int disX, int disY, int lookaheadX, int lookaheadY) {
		return !((disY == 11 || disY == 23) && model.isGhostNoUpIntersection(lookaheadX, lookaheadY));
	}
}
